package com.arc.spring.proxy.dynamic.builtin;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author YangYunhe
 * @date 2024/12/30
 * @description JDK 内置的代理功能 Proxy
 * 1、一定要有接口和实现类的存在，代理对象增强的是实现类在实现接口后重写的方法
 * 2、生成的代理对象只能转换成接口，不能转换成被代理类
 *      正确：Dinner dinnerProxy = (Dinner) Proxy.newProxyInstance()
 *      错误：Dinner dinnerProxy = (Person) Proxy.newProxyInstance()
 * 3、代理对象只能增强接口中定义的方法，实现类中其他和接口无关的方法是无法增强的
 * 4、代理对象只能读取到接口中方法上的注解，不能读取到实现类方法上的注解
 */
public class JdkBuiltinProxyDemo {

    public static void main(String[] args) {

        Dinner dinner = new Student("Tom");

        /*
         * Proxy 动态代理，获得代理对象后，在代理对象中对方法进行增强
         * ClassLoader loader 被代理的对象的类加载器
         * Class<?>[] interfaces 被代理对象实现的所有接口
         * InvocationHandler h 执行处理器对象，专门用于定义增强的规则
         */
        ClassLoader classLoader = dinner.getClass().getClassLoader();
        Class<?>[] interfaces = dinner.getClass().getInterfaces();
        Dinner dinnerProxy = (Dinner) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
            /**
             *
             * 代理对象调用被代理对象任何方法，都会触发 invoke() 方法
             * proxy 代理对象 dinnerProxy
             * method 被代理的方法（要增强的方法）eat()
             * args 被代理的方法运行时的实参 foodName
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("invoke() 方法执行了...");
                Object res = null;
                if (method.getName().equals("eat")) {
                    System.out.println("饭前洗手");
                    // 执行 eat() 方法
                    res = method.invoke(dinner, args);
                    System.out.println("饭后洗碗");
                } else {
                    res = method.invoke(dinner, args);
                }
                return res;
            }
        });

        dinnerProxy.eat("apples");
        System.out.println("----------------------------");
        dinnerProxy.drink();

    }

}

interface Dinner {

    void eat(String foodName);
    void drink();

}

class Person implements Dinner {

    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void eat(String foodName) {
        System.out.println("person " + name + " eats " + foodName);
    }

    @Override
    public void drink() {
        System.out.println("person " + name + " drink water");
    }
}

class Student implements Dinner {

    private String name;

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void eat(String foodName) {
        System.out.println("student " + name + " eats " + foodName);
    }

    @Override
    public void drink() {
        System.out.println("student " + name + " drink water");
    }
}
